﻿using System;
using System.Drawing;
using System.IO;
using System.Runtime.InteropServices;

namespace Util.Forms
{
    public static class Shell
    {
        /// <summary>
        /// Retrieves the shell icon for the given file extension.
        /// </summary>
        /// <param name="filename"></param>
        /// <returns></returns>
        public static Bitmap GetExtensionIcon(string extension)
        {
            SHFILEINFO info;
            IntPtr ret = ShellNativeMethods.SHGetFileInfo(
                "ABC." + extension, FileAttributes.Normal, out info,
                Marshal.SizeOf(typeof(SHFILEINFO)),
                SHGFI.UseFileAttributes | SHGFI.Icon | SHGFI.LargeIcon);
            if (ret == IntPtr.Zero)
                throw new InvalidOperationException("Failed to get icon.");

            try
            {
                return Bitmap.FromHicon(info.hIcon);
            }
            finally
            {
                ShellNativeMethods.DestroyIcon(info.hIcon);
            }
        }

    }

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    internal struct SHFILEINFO
    {
        public IntPtr hIcon;
        public int iIcon;
        [MarshalAs(UnmanagedType.U4)]
        public FileAttributes dwAttributes;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string szDisplayName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
        public string szTypeName;
    }

    [Flags]
    internal enum SHGFI
    {
        /// <summary>get icon</summary>
        Icon = 0x000000100,
        /// <summary>get display name</summary>
        DisplayName = 0x000000200,
        /// <summary>get type name</summary>
        TypeName = 0x000000400,
        /// <summary>get attributes</summary>
        Attributes = 0x000000800,
        /// <summary>get icon location</summary>
        IconLocation = 0x000001000,
        /// <summary>return exe type</summary>
        ExeType = 0x000002000,
        /// <summary>get system icon index</summary>
        SysIconIndex = 0x000004000,
        /// <summary>put a link overlay on icon</summary>
        LinkOverlay = 0x000008000,
        /// <summary>show icon in selected state</summary>
        Selected = 0x000010000,
        /// <summary>get only specified attributes</summary>
        Attr_Specified = 0x000020000,
        /// <summary>get large icon</summary>
        LargeIcon = 0x000000000,
        /// <summary>get small icon</summary>
        SmallIcon = 0x000000001,
        /// <summary>get open icon</summary>
        OpenIcon = 0x000000002,
        /// <summary>get shell size icon</summary>
        ShellIconSize = 0x000000004,
        /// <summary>pszPath is a pidl</summary>
        PIDL = 0x000000008,
        /// <summary>use passed dwFileAttribute</summary>
        UseFileAttributes = 0x000000010,
        /// <summary>apply the appropriate overlays</summary>
        AddOverlays = 0x000000020,
        /// <summary>Get the index of the overlay in the upper 8 bits of the iIcon</summary>
        OverlayIndex = 0x000000040,
    } 

    internal class ShellNativeMethods
    {
        [DllImport("shell32.dll", CharSet = CharSet.Auto)]
        internal static extern IntPtr SHGetFileInfo(
            string pszPath,
            [MarshalAs(UnmanagedType.U4)] FileAttributes dwFileAttributes, 
            out SHFILEINFO psfi, 
            int cbFileInfo, 
            [MarshalAs(UnmanagedType.U4)] SHGFI uFlags);

        [DllImport("user32.dll", ExactSpelling=true, SetLastError=true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool DestroyIcon(IntPtr hIcon);
    }
}
